/*
 * lua_environment.h
 * This file is part of OpenAnimator
 *
 * Copyright (C) 2008 - Abhinav Lele
 *
 * OpenAnimator is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAnimator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenAnimator; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

#include "common.h"

extern "C" {
#include "net/luasocket.h" // for lua network bindings
}

#include "xml/tinyxml.h"
#include "xml/xpath_processor.h"
#include "gl/luagl.h"      // for lua opengl  bindings

#include "sdl/SDL_image.h"
#include "luafont.h"

/* --srcext declarations --*/
/* srcext/xml */
extern "C" { int luaopen_xml_ltxml(lua_State* L); }




#ifndef __OA_LUA_ENVIRONMENT_H__
#define __OA_LUA_ENVIRONMENT_H__

/* --LUA Helpers-- */


/* Validation modules for lua APIs */


typedef int (*LUA_APIFN)(lua_State*);
enum LUALOADLIBS  { LUALIB_ALLBASE, LUALIB_BASE, LUALIB_TABLE, LUALIB_MATH, LUALIB_STRING, LUALIB_IO, LUALIB_DEBUG, LUALIB_XML, LUALIB_OPENGL, LUALIB_NET, LUALIB_FONT };
class LuaEnvironment {
	public:
		string lang_name,lang_ver;
		lua_State *L;
		int load_state[sizeof(LUALOADLIBS)];

		LuaEnvironment();
		//LuaEnvironment(int restricted);
		~LuaEnvironment();
		inline int call(string function_name) {
			lua_getglobal(L,function_name.c_str());
			if(lua_pcall(L,0,0,0)) ER("LuaEnvironment:call: " << "error while calling" << function_name << "\n" << lua_tostring(L,-1) << endl,-1)
			return 1;
		}
		inline int call(string function_name,string arg) {
			lua_getglobal(L,function_name.c_str());
			lua_pushstring(L,arg.c_str());
			if(lua_pcall(L,1,0,0)) ER("LuaEnvironment:call: " << "error while calling" << function_name << "\n" << lua_tostring(L,-1) << endl,-1)
			return 1;
		}
		inline int call(string function_name,double arg) {
			lua_getglobal(L,function_name.c_str());
			lua_pushnumber(L,arg);
			if(lua_pcall(L,1,0,0)) ER("LuaEnvironment:call: " << "error while calling" << function_name << "\n" << lua_tostring(L,-1) << endl,-1)
			return 1;
		}
		inline int call(string function_name,string arg1,double arg2) {
			lua_getglobal(L,function_name.c_str());
			lua_pushstring(L,arg1.c_str());
			lua_pushnumber(L,arg2);
			if(lua_pcall(L,2,0,0)) ER("LuaEnvironment:call: " << "error while calling" << function_name << "\n" << lua_tostring(L,-1) << endl,-1)
			return 1;
		}
		inline void create_link(LUA_APIFN fnptr, string luafnname) {
			lua_pushstring(L,luafnname.c_str());
			lua_pushcfunction(L, fnptr);
			lua_settable(L,LUA_GLOBALSINDEX);
		}
		inline void remove_link(string luafnname) {
			lua_pushnil(L);
			lua_setglobal(L,luafnname.c_str());
		}

		inline void remove_link(char *luafname) {
			lua_pushnil(L);
			lua_setglobal(L,luafname);
		}
		inline int load_library(LUALOADLIBS name) {
#define CALLX(FN,NAME)  { lua_pushcfunction(L,FN); lua_pushstring(L,NAME); if(lua_pcall(L,1,0,0)) return -1; }
			switch(name) {
				case LUALIB_ALLBASE:
					if(!load_state[LUALIB_ALLBASE])  luaL_openlibs(L); break;
				case LUALIB_BASE:
					if(!load_state[LUALIB_BASE])
						CALLX(luaopen_base,"luaopen_base")
					break;
				case LUALIB_TABLE:
					if(!load_state[LUALIB_TABLE])
						CALLX(luaopen_table,"luaopen_table")
					break;
				case LUALIB_MATH:
					if(!load_state[LUALIB_MATH])
						CALLX(luaopen_math,"luaopen_math")
			       		break;
				case LUALIB_STRING:
					if(!load_state[LUALIB_STRING])
						CALLX(luaopen_string,"luaopen_string")
					break;
				case LUALIB_IO:
					if(!load_state[LUALIB_IO])
					  	CALLX(luaopen_io,"luaopen_io");
					break;
				case LUALIB_DEBUG:
					if(!load_state[LUALIB_DEBUG])
					    	CALLX(luaopen_debug,"luaopen_debug");
					break;
				case LUALIB_XML: //TODO Test
					if(!load_state[LUALIB_XML])      this->load_library_xml(); break;
				case LUALIB_OPENGL: // TODO Test
					if(!load_state[LUALIB_OPENGL])   this->load_library_opengl(); break;
				case LUALIB_NET: //TODO Test
					if(!load_state[LUALIB_NET])      this->load_library_net(); break;
				case LUALIB_FONT:
					if(!load_state[LUALIB_FONT])	 this->load_library_font(); break;
				default:
					E("LuaEnvironment:load_library: Invalid Library Name") return -1; break;
			}
			return 1;
#undef CALLX
		}
		inline int reload_env() {
			M("RELOAD ENV")
			if(L) { lua_close(L); }
			L = NULL;
			L = lua_open();
			//cout << std::hex << L << std::dec <<  endl;
			if(L) return 1; else return -1;
		}
		//inline void load_library_xml() { if(!load_state[LUALIB_XML]) { load_state[LUALIB_XML]=1; luaopen_xml_ltxml(L); } }
		//inline void load_library_opengl() { if(!load_state[LUALIB_OPENGL]) { load_state[LUALIB_OPENGL] = 1; luaopen_opengl(L); } }
		protected:
		inline int load_library_net() {
			/*if(this->call("require","socket_core") == 1)	{	load_state[LUALIB_NET] = 1; return 1; } */
			luaopen_socket_core(L);
			return 1;
		}
		inline int load_library_xml() {
			luaopen_xml_ltxml(L);
			return 1; // no reason for failure
		}
		inline int load_library_opengl() {
			luaopen_opengl(L);
			return 1; //no readon for failure
		}
		inline int load_library_font() {
			this->create_link(XLUA_DrawText,"DrawText");
			return 1;
		}
		public:
		void load_restrictions(const char *filepath); /**< Loads restrictions from file. All banned operations appear on separate lines */

		inline int load_file(const char *path) {
			if(luaL_loadfile(L,path) != 0) { E( "LuaEnvironment:load_file: Load failed for " << path << endl ) return -1; }
			lua_pcall(L,0,0,0);
			return 1;
		}


};


/* --LUA Helpers End-- */


#endif
